package com.si.anddos.library.provider.rapidshare;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.sql.Date;
import java.util.regex.Pattern;

import com.si.anddos.dpadapters.Account;
import com.si.anddos.library.main.AccountDetail;
import com.si.anddos.library.provider.FileInfo;
import com.si.anddos.library.provider.FileState;
import com.si.anddos.library.provider.ServiceProvider;
import com.si.anddos.library.provider.ServiceProviderException;
import com.si.anddos.library.provider.ServiceProviderListener;
import com.si.anddos.utils.FileSaver;
import com.si.anddos.utils.NamedMatcher;
import com.si.anddos.utils.NamedPattern;
import com.si.anddos.utils.Utils;

/**
 * 
 * @author Carlos Martinez
 */

public class RapidshareDownloadProvider implements ServiceProvider {

	private ServiceProviderListener listener;
	private URL url;
	private FileSaver fileSaver;
	private final String DEFAULT_API_HOSTNAME = "api.rapidshare.com";
	private boolean downloadIsRunning = false;

	public RapidshareDownloadProvider(URL url) throws MalformedURLException {

		this.url = url;
	}

	public void download(long position) throws ServiceProviderException {

		download(null, position);
	}

	public void download(Account account, long position) throws ServiceProviderException {

		if (listener != null) {
			listener.downloadStart(this);
		}

		downloadIsRunning = true;
		
		InputStream responseStream = null;

		try {

			FileInfo fileInfo = getFileInfo(this.url);

			// URL for getting dlauth string
			URL url = createApiDownloadUrl(DEFAULT_API_HOSTNAME, fileInfo.getId(), fileInfo.getName(), account, null, position);

			System.out.println(url);

			URLConnection connection = url.openConnection();

			responseStream = connection.getInputStream();
			String response = Utils.inputStreamAsString(responseStream);

			System.out.println(response);

			if (response.startsWith("ERROR")) {
				throw new ServiceProviderException(response);
			}
			
			NamedMatcher matcher = firstRespodPattern.matcher(response);

			if (matcher.find()) {

				int countdown = Integer.parseInt(matcher.group("countdown"));

				if (listener != null) {
					listener.downloadWait(this, countdown);
				}

				Thread.sleep(countdown * 1000);

				// URL for retrieving file on specified position.
				URL downloadUrl = createApiDownloadUrl(
						matcher.group("hostname"), fileInfo.getId(),
						fileInfo.getName(), account, matcher.group("dlauth"), position);

				System.out.println(downloadUrl);

				connection = downloadUrl.openConnection();

				InputStream downloadStream = connection.getInputStream();

				byte[] buffer = new byte[4 * 1024];
				
				// Loop can be stopped by calling stopDownload() method which set downloadIsRunning.
				for (int bytes = 0; (bytes = downloadStream.read(buffer)) > 0 && downloadIsRunning;) {

					fileSaver.write(buffer, bytes);

					if (listener != null) {
						listener.downloadProgress(this, fileInfo.getSize(),	fileSaver.getPosition());
					}
				}
				
				if (listener != null) {
					if (downloadIsRunning) {
						listener.downloadFinish(this);
					}
					else {
						listener.downloadStop(this);
					}
				}
				
				downloadIsRunning = false;

			} else {
				throw new MalformedURLException();
			}

		} catch (ServiceProviderException e) {
			throw e;
		} catch (Exception e) {
			throw new ServiceProviderException(e);
		} finally {
			
			// Resources has to be freed in finally section!
			if (responseStream != null) {
				try {
					responseStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public void upload() {
		// TODO Auto-generated method stub
	}

	public void upload(Account account) {
		// TODO Auto-generated method stub
	}

	public void stopDownload() {

		downloadIsRunning = false;
	}

	public void pause(boolean isLogged) {

	}

	public void upload(File file) {
		// TODO Auto-generated method stub

	}

	public void upload(File file, String userName, String password) {
		// TODO Auto-generated method stub

	}

	public void upload(File file, Account user) {
		// TODO Auto-generated method stub

	}

	public void setServiceProviderListener(ServiceProviderListener listener) {

		this.listener = listener;
	}

	public URL getURL() {

		return url;
	}

	public void setFileSaver(FileSaver fileSaver) {

		this.fileSaver = fileSaver;
	}

	private static final NamedPattern fileInfoPattern = NamedPattern.compile(
					"^(?<id>[^,]*),(?<name>[^,]*),(?<size>[^,]*),(?<serverId>[^,]*),(?<status>[^,]*),(?<shortHost>[^,]*),(?<md5>[^,]*)$",
					Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
	
	public FileInfo getFileInfo() throws ServiceProviderException {
		return this.getFileInfo(this.url);
	}

	public FileInfo getFileInfo(URL url) throws ServiceProviderException {

		FileInfo fileInfo = null;
		
		InputStream responseStream = null;

		try {
			
			fileInfo = parseURL(this.url);
			
			URLConnection connection = createApiFileInfoUrl(fileInfo.getId(), fileInfo.getName()).openConnection();
			responseStream = connection.getInputStream();
			String response = Utils.inputStreamAsString(responseStream);

			System.out.println(response);

			if (response.startsWith("ERROR")) {
				throw new ServiceProviderException(response);
			}
			
			NamedMatcher matcher = fileInfoPattern.matcher(response);

			if (matcher.find()) {

				fileInfo.setSize(Long.parseLong(matcher.group("size")));
				fileInfo.setMd5("md5");

				switch (Integer.parseInt(matcher.group("status"))) {

				case 0:
					fileInfo.setState(FileState.NotFound);
					break;

				case 1:
					fileInfo.setState(FileState.OK);
					break;

				case 3:
					fileInfo.setState(FileState.ServerDown);
					break;

				case 4:
					fileInfo.setState(FileState.Illegal);
					break;

				case 5:
					fileInfo.setState(FileState.UserCountLimitExceeded);
					break;

				default:
					break;
				}
			}

		} catch (ServiceProviderException e) {
			throw e;
		} catch (Exception e) {
			throw new ServiceProviderException(e);
		} finally {
			if (responseStream != null) {
				try {
					responseStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		return fileInfo;
	}

	private static final NamedPattern keyValuePattern = NamedPattern.compile(
			"^(?<key>[^=]*)=(?<value>.*)$", Pattern.CASE_INSENSITIVE);

	public AccountDetail getAccountDetail(Account account)
			throws ServiceProviderException {

		final String apiUrl = "http://%s/cgi-bin/rsapi.cgi?sub=getaccountdetails_v1&login=%s&password=%s";

		try {

			URLConnection connection = new URL(String.format(apiUrl,
					DEFAULT_API_HOSTNAME,
					URLEncoder.encode(account.getUserName()),
					URLEncoder.encode(account.getPassword()))).openConnection();
			InputStream responseStream = connection.getInputStream();
			String response = Utils.inputStreamAsString(responseStream);

			System.out.println(connection.getURL());
			System.out.println(response);

			if (response.startsWith("ERROR")) {
				return null;
			}

			System.out.println(response);

			AccountDetail accountDetail = new AccountDetail();

			for (String line : response.split("\n")) {

				NamedMatcher matcher = keyValuePattern.matcher(line);

				if (matcher.find()) {

					String key = matcher.group("key");

					if (key.equals("email")) {
						accountDetail.setEmail(matcher.group("value"));
					} else if (key.equals("billeduntil")) {
						if (!matcher.group("value").equals("0")) {
							accountDetail.setBilledUntil(new Date(Long
									.parseLong(matcher.group("value")) * 1000));
						}
					} else if (key.equals("curspace")) {
						accountDetail.setCurrentSpace(Long.parseLong(matcher
								.group("value")));
					}
				}
			}

			return accountDetail;

		} catch (Exception e) {
			throw new ServiceProviderException(e);
		}
	}

	private URL createApiDownloadUrl(String hostName, String fileId,
			String fileName, Account account, String dlauth, long position)
			throws MalformedURLException {

		final String apiUrl = "http://%s/cgi-bin/rsapi.cgi?sub=download_v1&fileid=%s&filename=%s&start=%d";

		String urlString = String.format(apiUrl, hostName, fileId, fileName, position);

		if (account != null) {
			urlString += String.format("&login=%s&password=%s",
					URLEncoder.encode(account.getUserName()),
					URLEncoder.encode(account.getPassword()));
		}

		if (dlauth != null) {
			urlString += String.format("&dlauth=%s", dlauth);
		}

		return new URL(urlString);
	}

	private URL createApiFileInfoUrl(String fileId, String fileName)
			throws MalformedURLException {

		return new URL(
				String.format(
						"http://%s/cgi-bin/rsapi.cgi?sub=checkfiles_v1&files=%s&filenames=%s&incmd5=1",
						DEFAULT_API_HOSTNAME, fileId, fileName));
	}

	private static final NamedPattern filePattern = NamedPattern.compile(
			"^/files/(?<fileId>[^/]*)/(?<fileName>.*)$",
			Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);

	private static final NamedPattern firstRespodPattern = NamedPattern
			.compile(
					"^DL:(?<hostname>[^,]*),(?<dlauth>[^,]*),(?<countdown>[^,]*),(?<md5hex>[^,]*)$",
					Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);

	private FileInfo parseURL(URL url) throws MalformedURLException {

		FileInfo fileInfo = new FileInfo();
		NamedMatcher matcher = filePattern.matcher(url.getPath());

		if (matcher.find()) {
			fileInfo.setId(matcher.group("fileId"));
			fileInfo.setName(matcher.group("fileName"));
		} else {
			throw new MalformedURLException();
		}

		return fileInfo;
	}
}
